<template>
<div>
  <h1>赫夫曼编码模拟站</h1>
  <h2 class="time">{{time}}</h2>
  <div id="action">
    <el-dropdown trigger="click">
      <el-button type="primary" class="el-icon-s-management">
        压缩<i class="el-icon-arrow-down el-icon--right"></i>
      </el-button>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item @click.native="dialogFormVisible=true;form.isZip=true;">文字压缩</el-dropdown-item>
        <el-dropdown-item>
          <el-upload
              :http-request="zipFile"
              :on-success="clear"
              :before-upload="check"
              action="">
            文件压缩
          </el-upload>
        </el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>

    <el-dropdown trigger="click" class="clip">
      <el-button type="primary" class="el-icon-folder-opened">
        解压<i class="el-icon-arrow-down el-icon--right"></i>
      </el-button>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item @click.native="dialogFormVisible=true;form.isZip=false;">文字解压</el-dropdown-item>
        <el-dropdown-item>
          <el-upload
              :http-request="unZipFile"
              :on-success="clear"
              :before-upload="check"
              action="">
            文件解压
          </el-upload>
        </el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
  <div id="reflect">
    <table>
      <tr>
        <td v-for="code in reflectList" :key="code" >{{code}}</td>
      </tr>
      <tr>
        <td v-for="code in headList" :key="code" >{{code}}</td>
      </tr>
    </table>
  </div>
  <p class="tips">(仅支持ASCII码包含的字符,下方会展示出对应的赫夫曼树)</p>
  <hr/>
  <div style="display: flex;justify-content:center">
    <canvas  ref="canvas" width="6000" height="2500" :key="drawKey"></canvas>
  </div>
  <el-dialog :title="form.isZip?'文字压缩':'文字解压'" :visible.sync="dialogFormVisible" :before-close="close">
    <p class="tips">{{form.isZip?"(仅支持ASCII码包含的字符,不可输入中文或非半角字符)":"(输入二进制字符串)"}}</p>
    <el-form :model="form">
      <el-form-item >
        <el-input v-model="form.val" autocomplete="off"></el-input>
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="dialogFormVisible = false">取 消</el-button>
      <el-button type="primary" @click="confirm">确 定</el-button>
    </div>
  </el-dialog>
</div>


</template>

<script>
import {Notification} from 'element-ui';
import utilsStack from "@/util/utilsStack";
import axios from "axios";

export default {
  name: "huffman",
  data(){
    return{
      time:this.formatTime(),
      preArray:[],//前序序列字符串,
      root:undefined,
      maxDepth:0,
      depth:0,
      radius:20,
      lineArray:[],
      baseUrl:'http://localhost:8081',
      pattern:1,//1为压缩，2为解压
      fileName:"",
      huffmanDto:{},
      drawKey:0,
      headList:[],
      reflectList:[],
      dialogFormVisible:false,
      form:{
        val:"",
        isZip:true,
      }
    }
  },
  created() {
    this.preArray="81 33 16 8 # # 8 4 2 # # 2 # # 4 2 # # 2 # # 17 8 4 2 # # 2 1 # # 1 # # 4 2 1 # # 1 # # 2 1 # # 1 # # 9 # # 48 20 9 4 2 1 # # 1 # # 2 1 # # 1 # # 5 # # 11 5 2 1 # # 1 # # 3 # # 6 3 # # 3 # # 28 13 6 3 # # 3 # # 7 3 1 # # 2 # # 4 # # 15 # # ".split(" ");
  },
  mounted() {
    setInterval(()=>{
      this.time=this.formatTime();
    },1000)
    this.root=this.createTree();
    this.drawHuffmanTree();
    console.log("".charCodeAt(0));
    },
  methods:{
    formatTime(){
      let date=new Date();
      return `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}|${this.formatNumber(date.getHours())}:${this.formatNumber(date.getMinutes())}:${this.formatNumber(date.getSeconds())}`
    },
    formatNumber(str){
      return str.toString().length>=2?str.toString():'0'+str;
    },
    message(msg,type){
      Notification({
        title:'Tips',
        message:msg,
        type:type,
      })
    },
    createTree(){
      let stack=[];
      let isLeft=true;
      let count=0;
      let root=undefined;
      let depth=0;

      for(let i=0;i<this.preArray.length;i++){
        let ch=this.preArray[i];
        let node={val:ch};
        if (ch!=='#'){
          depth++;

          if (stack.length>0){
            isLeft?utilsStack.peek(stack).left=node:utilsStack.peek(stack).right=node;
          }

          node.depth=depth;
          stack.push(node);
          isLeft=true;
        } else{
          isLeft=false;
          count++;
        }

        if (count===2){
          count=0;
          stack.pop();
          depth--;
        }

        this.maxDepth=this.maxDepth<=depth?depth:this.maxDepth;

        while (stack.length>0&&utilsStack.peek(stack).left&&utilsStack.peek(stack).right){
         root=utilsStack.pop(stack);
         depth--;
        }

      }
      return root;
    },
    drawCircle(ctx,cfg,text){
      ctx.moveTo(cfg.x,cfg.y);
      ctx.fillText(text,cfg.x,cfg.y);
      ctx.moveTo(cfg.x+20,cfg.y);
      ctx.arc(cfg.x,cfg.y,this.radius,0,Math.PI*2,false);
    },
    preOrder(iniCfg,ctx,root){//树的层次遍历

      let queue=[];

      root.cfg=iniCfg;//根节点的初始化绘图配置

      queue.push(root);
      while (queue.length>0){
        let node=queue.shift();
        let depth=node.depth;
        let cfg=node.cfg;

        let clip=this.computeX(depth);//当前层数的节点横向间隔
        let vClip=this.computeY(depth);//当前层数与上一层数的纵向间隔

        this.lineArray.push(node.cfg);
        this.drawCircle(ctx,cfg,node.val)

        if (node.left){
          node.left.cfg={
            supX:cfg.x,
            supY:cfg.y,
            x:cfg.x-clip,
            y:cfg.y+vClip,
          }
          queue.push(node.left);
        }

        if (node.right){
          node.right.cfg={
            supX:cfg.x,
            supY:cfg.y,
            x:cfg.x+clip,
            y:cfg.y+vClip,
          }
          queue.push(node.right);
        }

      }
    },
    drawHuffmanTree(){
      let canvas=this.$refs.canvas;
      let ctx=canvas.getContext('2d');

      ctx.beginPath();
      //初始化画布配置
      ctx.lineWidth=2;
      ctx.strokeStyle='black';
      ctx.font='bold 20px Arial';
      ctx.textAlign='center'

      let width=canvas.width;

      let iniCfg={
        x:width/2,
        y:30,
      }


      this.preOrder(iniCfg,ctx,this.root);
      ctx.stroke();

      for(let i=0;i<this.lineArray.length;i++){
        let cfg=this.lineArray[i];
        ctx.moveTo(cfg.x,cfg.y);
        ctx.lineTo(cfg.supX,cfg.supY);
      }

      ctx.stroke();
    },
    computeX(depth){//计算每层节点的间隔
      return Math.pow(2,this.maxDepth-depth)*12;
    },
    computeY(depth){
      return depth*15;
    },
    // eslint-disable-next-line no-unused-vars
    clear(res,file,fileList){
      fileList=[];
    },
    check(file){
      if (file.type!=="text/plain"){
        this.$message.error("仅支持上传txt文件");
        return false;
      }
       return true;
    },
    reflect(){
      this.headList=[];
      this.reflectList=[];
      let codes=this.huffmanDto.codes;
      for (let code in codes){
        this.headList.push(codes[code]);

        if (Number.parseInt(code)===10){
          this.reflectList.push("\\n");
        }else if (Number.parseInt(code)===13) {
          this.reflectList.push("\\r")
        }else {
          this.reflectList.push(String.fromCharCode(Number.parseInt(code)));
        }

        console.log(codes[code],code,String.fromCharCode(Number.parseInt(code)))
      }
    },
    confirm(){
      let data=this.form.val;

     if (this.isValid(data,this.form.isZip)){
       if (this.form.isZip){
         this.zipString(data);
       }else {
         this.unZipString(data);
       }
       this.close();
     }else {
      this.message("错误的输入格式",'error');
     }
    },
    isValid(data,flag){
        for(let i=0;i<data.length;i++){
          let code=data.charCodeAt(i);
          console.log(code)
          console.log(flag)
          if (code>127||code<0&&flag){
            return false;
          }else if ((data.charAt(i)!=='0'&&data.charAt(i)!=='1')&&!flag){
            return false;
          }
        }
      return true;
    },
    close(){
      this.dialogFormVisible=false;
      this.form={
        val:"",
        isZip:true,
      }
    },
    zipString(str){
      let body={str:str};
      axios.post(this.baseUrl+"/zipString",body).then(res=>{
        this.huffmanDto=res.data;
        this.preArray=this.huffmanDto.preArray;
        console.log(res);
        this.reflect();
      })
    },
    zipFile(file){
      let fd=new FormData();
      this.fileName=file.file.name;

      fd.append("file",file.file);

      axios.post(this.baseUrl+"/zipDownload",fd).then(res=>{
        // this.huffmanDto=res.data;
        // this.preArray=this.huffmanDto.preArray;
        // // console.log(res);
        // this.reflect();
        this.download(res.data);
        console.log(res);
      })
    },
    unZipString(str){
      this.huffmanDto.data=str;
      axios.post(this.baseUrl+"/unZipString",this.huffmanDto).then(res=>{
        console.log(res);
      })
    },
    unZipFile(file){
      let fd=new FormData();
      this.fileName=file.file.name;

      fd.append("file",file.file);

      axios.post(this.baseUrl+"/unZipFile",fd).then(res=>{
        console.log(res);
        this.download(res.data)
      })
    },
    download(res){
        let url=window.URL.createObjectURL(new Blob([res]));
        let link=document.createElement('a');
        link.style.display='none';
        link.href=url;
        link.setAttribute("download",this.fileName);
        document.body.appendChild(link);
        link.click();
    }
  }
}
</script>

<style scoped>
#action{
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}
.font{
  font-size: 16px;
}
.time{
  margin: 20px 0;
}
.clip{
  margin-left: 20px;
}
.tips{
  font-size: 12px;
  color: grey;
}
#reflect{
  display: flex;
  justify-content: center;
}
#reflect table{
  border-spacing: 0;
}
#reflect table td{
  border: solid 1px black;
  padding: 15px 30px;
}
/deep/ .el-upload-list{
  display: none;
}

/deep/ .el-dialog__header {
  border-bottom: 1px solid #dadce0;
}
/deep/ .el-dialog {
  width: 50%;
  border-radius: 10px;
}
</style>